
/* GCSx
** BYTECODE.H
**
** Bytecode-related utilities/datatypes/structures
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_BYTECODE_H_
#define __GCSx_BYTECODE_H_

// Bits combine to form datatypes
enum {
    // Base types- all but VAR match with OM_
    DATA_VOID = -2,
    DATA_VAR = -1, // 0 reserved for none
    DATA_INT = 1,
    DATA_FLOAT = 2,
    DATA_STR = 3,
    DATA_ENTITY = 7,
    DATA_OBJECT = 8,
    
    // Flags
    DATA_ARRAY = 1,
    DATA_HASH = 2,
    DATA_CONST = 4,
    DATA_LOCAL = 8,
    DATA_GLOBAL = 16,
    DATA_PRIVATE = 32,
    DATA_PUBLIC = 64,
};

struct DataType {
    int baseType;
    int subType; // 0 for unapplicable OR unspecified/unknown
    int flags;
};

// datatype to wart conversion; doesn't consider local/global/private/public
// type C + baseType (A void B var D int E float etc.)
// followed by A + flags (A none B array C hash E const)
// followed by hexadecimal subtype (optional)
std::string dataTypeToWart(const DataType& dt);

// wart to datatype; returns pointer to first unused character within string,
// for reading next wart etc. returns NULL if error decoding wart
const char* wartToDataType(const char* wart, DataType& result);

// Labels within a codespace; also marks loops
// Labels cannot be accessed between workspaces
struct Label {
    int scope;
    int offset;
    int isLoop;
    // Stack depth at moment of label creation
    int stackDepth;
};

// Maps names to labels; names are newly-allocated char*s
// Blank name for anonymous loops
typedef hash_map<const char*, std::list<Label>, hash<const char*>, hash_eqstr> LabelMap;
void destroyLabelMap(LabelMap* map);
std::list<Label>& addLabelMap(LabelMap* map, const std::string& name);

// Functions listed within an individual script/etc
// Keyed off of name within a map
struct Function {
    int numparam;
    DataType* parameters; // All have LOCAL added
    DataType returntype;
    // -1 = unknown
    // during compile, offset is within functions section
    int offset; 
};

// Maps names to functions; names are newly-allocated char*s
typedef hash_map<const char*, Function, hash<const char*>, hash_eqstr> FunctionMap;
void destroyFunctionMap(FunctionMap* map);
Function& addFunctionMap(FunctionMap* map, const std::string& name);

// Variables listed within an individual script/etc
// Keyed off of name within a map
struct Variable {
    DataType datatype;
    int scope; // <0 global, 0 main local, >0 subscope (incl function param)
    union {
        Sint32 offs; // locals- stack offset
        Sint32 constI;
        BCfloat constF;
        const char* constS; // Allocated and owned
    } val;
    const char* name; // Backreference to name
};

// Maps names to varaibles; names are newly-allocated char*s
typedef hash_map<const char*, std::list<Variable>, hash<const char*>, hash_eqstr> VariableMap;
void destroyVariableMap(VariableMap* map);
// storeName is gauranteed to point to a valid string as long as map entry exists
std::list<Variable>& addVariableMap(VariableMap* map, const std::string& name, const char*& storeName);

// Linker entries (global variables, built-in functions, function libraries)
// Also used for global consts, to check consistency during linking
// Also used for local strings/functions/loops, but removed before final result
struct LinkEntry {
    enum LinkEntryType {
        LINK_GLOBAL = 1, // Link to global var's data
        LINK_GLOBALVAR, // Link directly to global var (may be different)
        LINK_BUILTIN,
        LINK_LIBRARY,
        LINK_CONST,
        LINK_STRING,
        LINK_FUNCTION,
        LINK_LOOP_CONTINUE,
        LINK_LOOP_END,
        // @TODO: links directly to other entity members/functions
    } type; // What type of link
    int offset; // Offset within bytecode to place/adjust ptr (ignored for const)
    std::string scope; // Library name, if specified (ignored for globals/local strings/functions/loops)
    std::string name; // Name of global/function/loop (ignored for local strings)
    // Combination of return/parameter types to verify at linking
    // For global variables, type
    // For global consts, includes const value
    // (ignored for local strings/functions/loops)
    std::string wart;
};

// Opcodes
enum Opcode {
    OP_NOOP = 0,
    // These may be alphabetical, but future opcodes add at end
    OP_ADD,
    OP_ADDf,
    OP_AND,
    OP_CONCAT,
    OP_CONVERT,
    OP_CREATEa,
    OP_CREATEe,
    OP_CREATEh,
    OP_CREATEo,
    OP_DEBUG,
    OP_DISCARD,
    OP_DISCARDL,
    OP_DIV,
    OP_DIVf,
    OP_EQ,
    OP_EQf,
    OP_EQo,
    OP_EQs,
    OP_GE,
    OP_GEf,
    OP_GEs,
    OP_GT,
    OP_GTf,
    OP_GTs,
    OP_IFFALSE,
    OP_IFFALSEa,
    OP_IFFALSEf,
    OP_IFFALSEh,
    OP_IFFALSEo,
    OP_IFFALSEs,
    OP_IFFALSEv,
    OP_IFTRUE,
    OP_IFTRUEa,
    OP_IFTRUEf,
    OP_IFTRUEh,
    OP_IFTRUEo,
    OP_IFTRUEs,
    OP_IFTRUEv,
    OP_INIT,
    OP_JUMP,
    OP_LE,
    OP_LEf,
    OP_LEs,
    OP_LT,
    OP_LTf,
    OP_LTs,
    OP_MOD,
    OP_MULT,
    OP_MULTf,
    OP_NE,
    OP_NEf,
    OP_NEo,
    OP_NEs,
    OP_OR,
    OP_PUSH,
    OP_PUSHa,
    OP_PUSHf,
    OP_PUSHh,
    OP_PUSHo,
    OP_PUSHs,
    OP_PUSHv,
    OP_RET,
    OP_SHIFTL,
    OP_SHIFTR,
    OP_STOP,
    OP_STORE,
    OP_STOREa,
    OP_STOREf,
    OP_STOREh,
    OP_STOREo,
    OP_STOREs,
    OP_STOREv,
    OP_SUB,
    OP_SUBf,
    OP_SUBR,
    OP_XOR,
    OP_GETe,
    OP_SETe,
    OP_GETo,
    OP_SETo,
    OP_IDLE,
    OP_RETVOID,
    OP_SETef,
    OP_SETei,
    OP_SETof,
    OP_SEToi,
    // (not an actual opcode)
    OP_LAST,
};

// O)perand addressing M)odes
// Most bit combinations are valid
// - Unused object types are reserved (types 9-15)
// - Array/hash literals not supported (types 5-6)
// - Reserved literals not supported (types 10-15)
// - Cannot use OM_INDIRECT with entry, array, hash, local, global
// - Cannot use OM_POP with entry, non-const, or convert
// - Const and copy go together; non-const and convert go together
enum OperMode {
    // Non-literals
    OM_ENTRY = 0, // Represents a variant
    OM_INT = 1,
    OM_FLOAT = 2,
    OM_STR = 3,
    OM_STR_CONST = 4,
    OM_ARRAY = 5,
    OM_HASH = 6,
    OM_ENTITY = 7,
    OM_OBJECT = 8,
    // (these types are reserved)
    OM_RESERVED_1 = 9,
    OM_RESERVED_2 = 10,
    OM_RESERVED_3 = 11,
    OM_RESERVED_4 = 12,
    OM_RESERVED_5 = 13,
    OM_RESERVED_6 = 14,
    OM_RESERVED_7 = 15,

    // Literals
    OM_NONE = 0,
    // OM_INT also used as literal for offsets
    // OM_FLOAT
    // [OM_STR not legal but used during compilation]
    // OM_STR_CONST
    // [OM_ARRAY / OM_HASH reserved for literals later]
    OM_ALL = 7, // entity, always w/subtype even if 0
    OM_ALL_OTHER = 8, // entity, always w/subtype even if 0
    OM_NOTHING = 9, // entity or object, no subtype
    OM_THIS = 10, // entity, no subtype
    // 11-14 reserved; currently assumed to be other OBJ_ literals
    OM_POINTER = 15,
    
    // Location
    OM_POP = 0, // Must always use with one of two flags
    OM_STACK = 16,
    OM_LOCAL = 32,
    OM_GLOBAL = 48,
    
    // Use with pop/stack to indicate stack entry is a POINTER
    // directly within an array/hash to the noted type, NO CONVERSION
    OM_INDIRECT = 192,
    
    // Flags
    // If present type is preknown and no conversion needed
    // If not, item is converted 'in place' if necessary
    // Converting an array or hash makes a new copy, but this
    // will NOT make a new copy if type is already correct
    OM_NO_CONVERT = 64,
    // If conversion performed, don't affect original
    // Also *gaurantees* a new array/hash copy (no ref)
    OM_COPY = 128, 
    
    // Masks
    OM_BASETYPE = 15,
    OM_LOCATION = 48,
    OM_FLAGS = 192,
};

// Use these to ensure proper handling due to overlapping operand mode bits
// These may not be used in bytecode interpreting for speed/efficiency reasons

#define OM_IS_NONE(x)       ((x) == OM_NONE)
#define OM_IS_OFFSET(x)     ((x) == OM_INT)
#define OM_IS_POINTER(x)    ((x) == OM_POINTER)

#define OM_IS_LITERAL(x)    (((x) & OM_BASETYPE) == (x))

#define OM_IS_INT(x)        (((x) & OM_BASETYPE) == OM_INT)
#define OM_IS_FLOAT(x)      (((x) & OM_BASETYPE) == OM_FLOAT)
#define OM_IS_STR(x)        (((x) & OM_BASETYPE) == OM_STR || ((x) & OM_BASETYPE) == OM_STR_CONST)
#define OM_IS_CONST(x)      (((x) & OM_BASETYPE) == OM_STR_CONST)
// Be prepared for literal arrays/hashes in the future
#define OM_IS_ARRAY(x)      (((x) & OM_BASETYPE) == OM_ARRAY)
#define OM_IS_HASH(x)       (((x) & OM_BASETYPE) == OM_HASH)
// Always check for literal status first before anything further
#define OM_IS_ENTITY(x)     (((x) & OM_BASETYPE) == OM_ENTITY)
#define OM_IS_OBJECT(x)     (((x) & OM_BASETYPE) == OM_OBJECT)
// (or can check for 'none' instead of literal, before this one)
#define OM_IS_ENTRY(x)      (((x) & OM_BASETYPE) == OM_ENTRY)

#define OM_IS_LOCAL(x)      (((x) & OM_LOCATION) == OM_LOCAL)
#define OM_IS_STACK(x)      (((x) & OM_LOCATION) == OM_STACK)
#define OM_IS_GLOBAL(x)     (((x) & OM_LOCATION) == OM_GLOBAL)
#define OM_IS_POP(x)        (((x) & OM_LOCATION) == OM_POP && ((x) & OM_BASETYPE) != (x))

#define OM_IS_INDIRECT(x)   (((x) & OM_FLAGS) == OM_INDIRECT)
#define OM_IS_KNOWN(x)      (((x) & OM_FLAGS) == OM_NO_CONVERT)
#define OM_IS_CONVERT(x)    (((x) & OM_FLAGS) != OM_NO_CONVERT && ((x) & OM_FLAGS) != OM_INDIRECT)
#define OM_IS_COPY(x)       (((x) & OM_FLAGS) == OM_COPY)

#ifdef COMPILEASSERT
// Checks for valid opcode/mode combinations
void checkOpcode(Opcode opc, Uint8 mode1, Uint8 mode2, Uint8 mode3);
#endif

// Dumps program to debug (uses static globals, not thread-safe)
void decompileBytecode(const Uint32* bytecode, const std::list<LinkEntry>* links);
int debugBytecode(std::string& line, const Uint32* bytecode);

#endif
